package org.hagopa.games.moneypool.game;

import java.io.IOException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.hagopa.games.moneypool.dblayer.GameGeneralInfoAdopt;
import org.hagopa.games.moneypool.msgs.CommandsEnum;
import org.hagopa.games.moneypool.msgs.Message;
import org.hagopa.games.moneypool.msgs.ContentTypes.ChatMsg;
import org.hagopa.games.moneypool.msgs.ContentTypes.game.StartTurn;
import org.hagopa.games.moneypool.msgs.ContentTypes.service.StatusData;
import org.hagopa.games.moneypool.network.ClientStatus;
import org.hagopa.games.moneypool.network.NetClientHandler;

public class GameThread implements Runnable{
	private long index = 0;
	private long gameId = 0;
	private int currPlayerIndex = 1;
	//private DiesPair localDies = new DiesPair();
	private int activePlayer = 1; 
	private Selector sel = null;
	private boolean isRunning = false;
	private List <PlayerHolder> waitingPlayers = null;
	private GameProcedure GP = null;
	private GameGeneralInfoAdopt gdata = null;
	
	
	public GameThread(long id, GameGeneralInfoAdopt gId){
		this.index = id;
		this.gdata = gId;
				System.out.println("Thread Id: " + Thread.currentThread().getId());
				System.out.println("thread: " +System.identityHashCode(Thread.currentThread()));
		waitingPlayers = new ArrayList<PlayerHolder>();
		this.GP =  new GameProcedure(this.gdata);
		
		try {
			sel = Selector.open();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	public void run() 
	{
		System.out.println("New Game Running!!!! Id:" + gameId + " index: " + index);
		
		GP.init();
		
		this.isRunning = true;
		
		mainLoop();
	}
	
	public void mainLoop()
	{
		System.out.println("GameThread MainLoop");
		while(this.isRunning)
		{
			
			int num = 0;
			int counter = 0;
			try {
				
				num = sel.select();
			} catch (IOException e) {
				
				e.printStackTrace();
			}
			if(num == 0)
				continue;
			Set<SelectionKey> keys = Collections.synchronizedSet(sel.selectedKeys());
			
			Iterator<SelectionKey> it = keys.iterator();
			while(it.hasNext())
			{
				
				SelectionKey skey = (SelectionKey)it.next();
				it.remove();
				
				if (skey.isValid() && skey.isReadable())
				{
						//System.out.println("\n---Inside GameThread---\n-------\nReadable!!!");
						synchronized (this.waitingPlayers) {
						
							Iterator<PlayerHolder> ph = this.waitingPlayers.iterator();
							
							while(ph.hasNext())
							{
								counter++;
								PlayerHolder tmp = ph.next();
								if(((NetClientHandler)tmp.getClientHandler()).getChannel().isConnected()
										&& ((NetClientHandler)tmp.getClientHandler()).getChannel().socket().isConnected())
								{
									if(skey.channel().equals(((NetClientHandler)tmp.getClientHandler()).getChannel()))
									{
										System.out.println("[GameThread] Channel Found");
										if(null != ((NetClientHandler)tmp.getClientHandler()).getMsg())
										{
											responseToClient(tmp);
										}
										else
										{
											System.out.println(" [GameThread] Player No " +tmp.getGId() + " disconnected");
											skey.cancel();
											try {
												skey.channel().close();
												} catch (IOException e) {
													// TODO Auto-generated catch block
													e.printStackTrace();
												}
												removePlayer(tmp);
										}
									}
									
								}
								else
								{
									System.out.println("Socket is not Connected!!!!!");
									skey.cancel();
									try {
										skey.channel().close();
										} catch (IOException e) {
											// TODO Auto-generated catch block
											e.printStackTrace();
										}
								}
							}
							
						}
					}
					else
					{
						System.out.println("Key is not Connectable!!!!!");
						skey.cancel();
						
					}
				
					if(counter == 0)
						this.isRunning = false;
			}
		
			keys.clear();
			if(this.waitingPlayers.size() == this.gdata.getMax_players())
			{// i'm not sure that is here the best point to send StartTurn
				sendStartTurn();
			}
		}
	}
	
	private void responseToClient(PlayerHolder newPlayer)
	{
		if(null == newPlayer)
		{
			System.out.println("Playerholder is Empty");
			return;
		}
		Message tmp = ((NetClientHandler)newPlayer.getClientHandler()).getLastClientMsg();
		if(tmp == null)
		{
			System.out.println("Failed to get Message");
			return;
		}
		
		System.out.println("Last Command:" + ((NetClientHandler)newPlayer.getClientHandler()).getLastClientMsg().getCommand().idx());
		System.out.println("Chat command: " + CommandsEnum.CHAT_MSG.idx() );
		 if(CommandsEnum.CHAT_MSG.idx() == ((NetClientHandler)newPlayer.getClientHandler()).getLastClientMsg().getCommand().idx())
		 {
			Message reply = new Message();
			reply.setClientId(((NetClientHandler)newPlayer.getClientHandler()).getLastClientMsg().getClientId());
			reply.setSessionId(((NetClientHandler)newPlayer.getClientHandler()).getLastClientMsg().getSessionId());
			reply.setCommand(CommandsEnum.CHAT_MSG);
			//ChatMsg cmsg = new ChatMsg();
//			cmsg.setSenderId(((ChatMsg)((NetClientHandler)newPlayer.getClientHandler()).getLastClientMsg().getContentHandler()).getSenderId());
//			cmsg.unmarshallFromByte((((ChatMsg)((NetClientHandler)newPlayer.getClientHandler()).getLastClientMsg().getContentHandler()).marshallToByte()));
			//System.out.println("before Chat BroadCast. Sender Id: " + cmsg.getSenderId());
			reply.setContentSz(reply.getContentSz());
			reply.setContentHandler(((ChatMsg)((NetClientHandler)newPlayer.getClientHandler()).getLastClientMsg().getContentHandler()));
			((NetClientHandler)newPlayer.getClientHandler()).setLastClientMsg(reply);
			 broadcastChatMsg(reply);
		 }
		 else if(CommandsEnum.READY.idx() == ((NetClientHandler)newPlayer.getClientHandler()).getLastClientMsg().getCommand().idx())
			{
			 Message msg4Client = new Message();
				msg4Client.setClientId(((NetClientHandler)newPlayer.getClientHandler()).getLastClientMsg().getClientId());
				msg4Client.setSessionId(((NetClientHandler)newPlayer.getClientHandler()).getLastClientMsg().getSessionId());
				msg4Client.setCommand(CommandsEnum.UPDATE);
				StatusData sdata = new StatusData();
				sdata.setMode(181);
				//
				msg4Client.setContentSz(8);
				msg4Client.setContentHandler(sdata);
				((NetClientHandler)newPlayer.getClientHandler()).setMsg4Client(msg4Client);
				((NetClientHandler)newPlayer.getClientHandler()).sendMsg(msg4Client.marshall());
				
			}
		 else
		 {
			 this.GP.getPlayerAction(newPlayer);
			 if(null != ((NetClientHandler)newPlayer.getClientHandler()).getMsg4Client())
			 {
				 broadcastSend(((NetClientHandler)newPlayer.getClientHandler()).getMsg4Client());
			 }
		 }

	}
	
	public void sendStartTurn()
	{
		System.out.println("Sending Start Turn to To Next Player!!!");
		Iterator<PlayerHolder> itr = this.waitingPlayers.iterator();
		Message msg = new Message();
		while(itr.hasNext())
		{
			PlayerHolder tmp = itr.next();
			if(tmp.getGId() == activePlayer)
			{
				msg.setClientId(((NetClientHandler)tmp.getClientHandler()).getClientId());
				msg.setSessionId(((NetClientHandler)tmp.getClientHandler()).getSessionId());
				msg.setCommand(CommandsEnum.UPDATE);
				
				StartTurn st = new StartTurn();
				
				msg.setContentSz(4);
				msg.setContentHandler(st);
				((NetClientHandler)tmp.getClientHandler()).setMsg4Client(msg);
				((NetClientHandler)tmp.getClientHandler()).sendMsg(msg.marshall());
				return;
			}
		}
		
			
	}
	public void appendPlayerToGame(PlayerHolder newPlayer)
	{
		// Player starts with 500 tugriks of Cash
		//if player type is active (1) we append him to game, and give him game seq number
		// If it is a network client and not AIPlayer we send response (for a while)
		
		int startCash = 500;
		if(null == newPlayer)
		{
			System.out.println("Empty Player Holder");
			return;
		}
		System.out.println("PLayers counter: " + currPlayerIndex);
		if(newPlayer.getType() == 1)
		{
			
			if(currPlayerIndex > 6)
				return;
			newPlayer.setGId(currPlayerIndex);
			currPlayerIndex++;
			newPlayer.setCash(startCash);
			
			
			//newPlayer.setGId(currPlayerIndex);
			System.out.println("Added New Player:" + newPlayer.getGId());
			System.out.println("To Gane Id: " + ((NetClientHandler)newPlayer.getClientHandler()).getGameId());
			((NetClientHandler)newPlayer.getClientHandler()).setStatus(ClientStatus.PLAYING);
			this.waitingPlayers.add(newPlayer);
			if(newPlayer.isNetClient())
			{
				//sendStartTurn();
				responseToClient(newPlayer);
				try 
				{
					((NetClientHandler)newPlayer.getClientHandler()).getChannel().register(this.sel, SelectionKey.OP_READ);
					
				}
				catch (ClosedChannelException e) 
				{
					e.printStackTrace();
				}
				//responseToClient(newPlayer);
			}
			else{
				System.out.println("Not Connected, or not Network Client");
			}
			
		}
	}
	private void removePlayer(PlayerHolder player)
	{
		System.out.println("Remove Player: " + player.getGId() + "client Id: " + ((NetClientHandler)player.getClientHandler()).getClientId());
	}
	public void broadcastChatMsg(Message msg)
	{
		Iterator<PlayerHolder> players = this.waitingPlayers.iterator();
		
		while(players.hasNext())
		{
			PlayerHolder tmp = players.next();
			
			System.out.println("Msg Owner Id:" + ((ChatMsg)msg.getContentHandler()).getSenderId());
			if(tmp.getGId() != ((ChatMsg)msg.getContentHandler()).getSenderId())
			{
				msg.setClientId(((NetClientHandler)tmp.getClientHandler()).getClientId());
				msg.setSessionId(((NetClientHandler)tmp.getClientHandler()).getSessionId());
				System.out.println("Sending to player:" + tmp.getGId());
				
				((NetClientHandler)tmp.getClientHandler()).sendMsg(msg.marshall());
			}
		}
	}
	public void broadcastSend(Message msg )
	{
		System.out.println("Broadcasting :" + msg.getCommand().getName() + "[" + msg.getCommand().idx() + "]");
		
		Iterator<PlayerHolder> players = this.waitingPlayers.iterator();
		
		while(players.hasNext())
		{
			PlayerHolder tmp = players.next();
				
			msg.setClientId(((NetClientHandler)tmp.getClientHandler()).getClientId());
			msg.setSessionId(((NetClientHandler)tmp.getClientHandler()).getSessionId());
			
			((NetClientHandler)tmp.getClientHandler()).sendMsg(msg.marshall());
		}
	}
	
	public boolean isRunning() {
		return isRunning;
	}
	public void setRunning(boolean isRunning) {
		this.isRunning = isRunning;
	}
}
